// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build aix | | dragonfly | | | freebsd | | | | | js&wasm | | linux | | netbsd | | | openbsd | | solaris 

package os

import (
	"io"
	"runtime"
	"sync"
	"syscall"
	"unsafe"
)

// 如果文件描述目录，则提供辅助信息
type dirInfo struct {
	buf  *[]byte // 目录I/O缓冲区
	nbuf int     // buf长度；Getdirentries 
	bufp int     // 返回值buf中下一条记录的位置。
}

const (
	// 要解决https问题，超过5760个：
	blockSize = 8192
)

var dirBufPool = sync.Pool{
	New: func() any {
		// 缓冲区必须至少有一个块长。
		buf := make([]byte, blockSize)
		return &buf
	},
}

func (d *dirInfo) close() {
	if d.buf != nil {
		dirBufPool.Put(d.buf)
		d.buf = nil
	}
}

func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
	// 如果此文件没有dirinfo，请创建一个。
	if f.dirinfo == nil {
		f.dirinfo = new(dirInfo)
		f.dirinfo.buf = dirBufPool.Get().(*[]byte)
	}
	d := f.dirinfo

	// 为下面的实现更改n的含义。
	// 
	// 上面的n是“如果n<=0，
	// Readdir返回一个
	// 单片目录中的所有文件信息”的公共接口。
	// 
	// 但在下面，我们只使用负表示循环，直到
	// 结束，正表示有界，正
	// 终止于0。
	if n == 0 {
		n = -1
	}

	for n != 0 {
		// 必要时重新填充缓冲区
		if d.bufp >= d.nbuf {
			d.bufp = 0
			var errno error
			d.nbuf, errno = f.pfd.ReadDirent(*d.buf)
			runtime.KeepAlive(f)
			if errno != nil {
				return names, dirents, infos, &PathError{Op: "readdirent", Path: f.name, Err: errno}
			}
			if d.nbuf <= 0 {
				break // EOF 
			}
		}

		// 排空缓冲区
		buf := (*d.buf)[d.bufp:d.nbuf]
		reclen, ok := direntReclen(buf)
		if !ok || reclen > uint64(len(buf)) {
			break
		}
		rec := buf[:reclen]
		d.bufp += int(reclen)
		ino, ok := direntIno(rec)
		if !ok {
			break
		}
		if ino == 0 {
			continue
		}
		const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
		namlen, ok := direntNamlen(rec)
		if !ok || namoff+namlen > uint64(len(rec)) {
			break
		}
		name := rec[namoff : namoff+namlen]
		for i, c := range name {
			if c == 0 {
				name = name[:i]
				break
			}
		}
		// 在分配字符串之前检查无用的名称。
		if string(name) == "." || string(name) == ".." {
			continue
		}
		if n > 0 { // 请参见上面的'n==0'注释
			n--
		}
		if mode == readdirName {
			names = append(names, string(name))
		} else if mode == readdirDirEntry {
			de, err := newUnixDirent(f.name, string(name), direntType(rec))
			if IsNotExist(err) {
				// 文件在readdir和stat之间消失。
				// 将其视为不存在。
				continue
			}
			if err != nil {
				return nil, dirents, nil, err
			}
			dirents = append(dirents, de)
		} else {
			info, err := lstat(f.name + "/" + string(name))
			if IsNotExist(err) {
				// 文件在readdir+stat之间消失。
				// 将其视为不存在。
				continue
			}
			if err != nil {
				return nil, nil, infos, err
			}
			infos = append(infos, info)
		}
	}

	if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
		return nil, nil, nil, io.EOF
	}
	return names, dirents, infos, nil
}

// readInt返回偏移量off处本机字节顺序的大小字节无符号整数。
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
	if len(b) < int(off+size) {
		return 0, false
	}
	if isBigEndian {
		return readIntBE(b[off:], size), true
	}
	return readIntLE(b[off:], size), true
}

func readIntBE(b []byte, size uintptr) uint64 {
	switch size {
	case 1:
		return uint64(b[0])
	case 2:
		_ = b[1] // 编译器的边界检查提示；见戈朗。org/issue/14808 
		return uint64(b[1]) | uint64(b[0])<<8
	case 4:
		_ = b[3] // 编译器的边界检查提示；见戈朗。org/issue/14808 
		return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
	case 8:
		_ = b[7] // 编译器的边界检查提示；见戈朗。org/issue/14808 
		return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
			uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
	default:
		panic("syscall: readInt with unsupported size")
	}
}

func readIntLE(b []byte, size uintptr) uint64 {
	switch size {
	case 1:
		return uint64(b[0])
	case 2:
		_ = b[1] // 编译器的边界检查提示；见戈朗。org/issue/14808 
		return uint64(b[0]) | uint64(b[1])<<8
	case 4:
		_ = b[3] // 编译器的边界检查提示；见戈朗。org/issue/14808 
		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
	case 8:
		_ = b[7] // 编译器的边界检查提示；见戈朗。org/issue/14808 
		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
			uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
	default:
		panic("syscall: readInt with unsupported size")
	}
}
